package com.tarena.lbs.basic.web.repository;


import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.BasePageList;
import com.tarena.lbs.basic.web.mapper.StoreMapper;
import com.tarena.lbs.pojo.basic.bo.StoreBO;
import com.tarena.lbs.pojo.basic.dos.StoreDO;
import com.tarena.lbs.pojo.basic.param.StoreParam;
import com.tarena.lbs.pojo.basic.param.UserLocationParam;
import com.tarena.lbs.pojo.basic.po.StorePO;
import com.tarena.lbs.pojo.basic.query.AreaStoreQuery;
import com.tarena.lbs.pojo.basic.query.StoreQuery;
import lombok.extern.slf4j.Slf4j;

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.stereotype.Repository;


import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Repository
public class StoreRepository {
    @Autowired
    private StoreMapper storeMapper;
    public BasePageList<StoreBO> storeList(StoreQuery query) {
        log.info("查询分页的入参:{}",query);
        //1.开启分页
        PageHelper.startPage(query.getPageNo(),query.getPageSize());
        //2.sql select
        List<StoreDO> dos = storeMapper.selectStoresByPage(query);
        //3.封装转化返回
        if (CollectionUtils.isNotEmpty(dos)){
            PageInfo<StoreDO> pageDos = new PageInfo<>(dos);
            List<StoreBO> storeBOS = pageDos.getList().stream().map(storeDO -> {
                StoreBO storeBO = new StoreBO();
                BeanUtils.copyProperties(storeDO,storeBO);
                return storeBO;
            }).collect(Collectors.toList());
            return new BasePageList<>(storeBOS,pageDos.getTotal());
        }
        return new BasePageList<>(null,0L);
    }

    public Integer saveStore(StoreParam storeParam) throws IOException {
        //1.db es存 都转成DO 来存储 保证数据 可用的 缺少 填补
        StoreDO storeDO = new StoreDO();
        //1.1 字段相同 拷贝
        BeanUtils.copyProperties(storeParam,storeDO);
        //1.2 时间填补(应该业务使用BO bo传给仓库或转成do)
        storeDO.setCreateTime(new Date());
        storeDO.setUpdateTime(storeDO.getCreateTime());
        storeDO.setStoreLatitude(storeParam.getLatitude());
        storeDO.setStoreLongitude(storeParam.getLongitude());
        GeoPoint geoPoint = new GeoPoint(Double.valueOf(storeParam.getLatitude()),Double.valueOf(storeParam.getLongitude()));
        storeDO.setLocation(geoPoint);
        //写入数据库 获取id
        storeMapper.insertDb(storeDO);
        //写入es
        insertES(storeDO);
        return storeDO.getId();
    }
    @Autowired
    private RestHighLevelClient client;
    private void insertES(StoreDO storeDO) throws IOException {
        IndexRequest indexRequest = new IndexRequest("lbs_store");
        //指定索引,指定id,转化数据成为json 写入文档doc
        indexRequest.id(storeDO.getId().toString());
        //填写 文档具体数据 storeDO转化成json
        String jsonString = JSON.toJSONString(storeDO);
        indexRequest.source(jsonString, XContentType.JSON);
        //执行请求
        client.index(indexRequest, RequestOptions.DEFAULT);
    }

    public BasePageList<StoreBO> getStoresByAreasAndBusiness(Integer id, AreaStoreQuery query) {
        PageHelper.startPage(query.getPageNo(),query.getPageSize());
        List<StoreDO> dos = storeMapper.selectStoresByAreasAndBusiness(id,query);
        if (CollectionUtils.isNotEmpty(dos)){
            PageInfo<StoreDO> pageDos = new PageInfo<>(dos);
            List<StoreBO> bos = pageDos.getList().stream().map(storeDO -> {
                StoreBO bo =new StoreBO();
                BeanUtils.copyProperties(storeDO,bo);
                return bo;
            }).collect(Collectors.toList());
            return new BasePageList<>(bos,pageDos.getTotal());
        }
        return new BasePageList<>(null,0L);
    }

    public List<StorePO> getStoresByBizId(Integer id) {
        //select * from lbs_store where business_id = #{id}
        QueryWrapper<StorePO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("business_id",id);
        return storeMapper.selectList(queryWrapper);
    }

    public StoreBO getStoreByLocationRadius(UserLocationParam param, double distanceValue) throws BusinessException {
        //RestHighLevelClient的api query GeoDistanceQuery
        //1.构造一个请求
        SearchRequest request = new SearchRequest("lbs_store");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //2.封装请求中的各种查询参数
        //2.1 只取一个值
        sourceBuilder.from(0);
        sourceBuilder.size(1);
        //2.2 地理位置的查询条件
        org.elasticsearch.common.geo.GeoPoint geoPoint = new org.elasticsearch.common.geo.GeoPoint(Double.valueOf(param.getLatitude()), Double.valueOf(param.getLongitude()));
        GeoDistanceQueryBuilder query = QueryBuilders.geoDistanceQuery("location");
        query.point(geoPoint).distance(distanceValue, DistanceUnit.KILOMETERS);
        sourceBuilder.query(query);
        request.source(sourceBuilder);
        //3.拿到响应
        try{
            SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
            log.info("当前定位数据:{},一共查到{}条数据",param,searchResponse.getHits().getTotalHits().value);
            if (searchResponse.getHits().getTotalHits().value>0){
                //可以封装返回 一个storeBO
                SearchHit hit=searchResponse.getHits().getHits()[0];
                //当初下入的json 将json读取转化会do 封装成bo返回
                StoreDO storeDO = JSON.parseObject(hit.getSourceAsString(), StoreDO.class);
                StoreBO bo=new StoreBO();
                BeanUtils.copyProperties(storeDO,bo);
                log.info("取到的store是:{}",bo);
                return bo;
            }
        }catch (Exception e){
            log.error("查询范围内的店铺 失败",e);
            throw new BusinessException("-2","定位上报查询失败");
        }
        return null;
    }
}
